package com.gsi.webstore.platform.storefront.internal.order;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import com.gsi.webstore.platform.adapter.taxes_duties_fees.capi.calculation.ComputedErrorItem;
import com.gsi.webstore.platform.adapter.taxes_duties_fees.capi.calculation.ComputedTaxInfoItem;
import com.gsi.webstore.platform.storefront.capi.calculation.GSINetBasedCalculation_V2;
import com.gsi.webstore.platform.storefront.capi.order.GSIExtraChargeInformationBO;
import com.gsi.webstore.platform.storefront.capi.order.GSILineItemCtnrCalculationResultView;
import com.gsi.webstore.platform.storefront.capi.order.GSITaxInformationBO;
import com.intershop.beehive.bts.capi.orderprocess.ProductLineItem;
import com.intershop.beehive.bts.capi.orderprocess.calculation.ComputedProductLineItem;
import com.intershop.beehive.bts.capi.payment.PaymentInstrumentInfo;
import com.intershop.beehive.bts.capi.payment.calculation.ComputedPaymentInstrumentItem;
import com.intershop.beehive.foundation.quantity.Money;
import com.intershop.component.foundation.capi.spreadsheet.CellSelector;
import com.intershop.component.foundation.capi.spreadsheet.CellSelector.Group;
import com.intershop.component.foundation.capi.spreadsheet.CellSelector.GroupSet;
import com.intershop.component.foundation.capi.spreadsheet.CellSelector.SubGroup;
import com.intershop.component.foundation.capi.spreadsheet.ComputedItem;
import com.intershop.component.foundation.capi.spreadsheet.ComputedMoneyItem;
import com.intershop.component.foundation.capi.spreadsheet.ComputedPercentageItem;
import com.intershop.component.marketing.capi.promotion.calculation.rule.ComputedDiscountItem;
import com.intershop.component.marketing.capi.promotion.calculation.rule.ComputedDynamicMessageDiscountItem;
import com.intershop.component.shipping.capi.shippingchargecalculator.ComputedShippingBucketItem;
import com.intershop.component.shipping.capi.shippingchargecalculator.ComputedShippingExtraChargeItem;
import com.intershop.sellside.appbase.b2c.capi.calculation.BaseCalculationResultView;
import com.intershop.sellside.appbase.b2c.capi.calculation.LineItemCtnrCalculationResultViewProxy;
import com.intershop.sellside.appbase.b2c.capi.order.CostInformationBO;
import com.intershop.sellside.appbase.b2c.capi.order.Discount;
import com.intershop.sellside.appbase.b2c.internal.order.CostInformationBOImpl;

public class GSINetBasedCalculationResultView_V2 extends LineItemCtnrCalculationResultViewProxy implements GSILineItemCtnrCalculationResultView
{
    public final BaseCalculationResultView _delegate;

    public GSINetBasedCalculationResultView_V2(final BaseCalculationResultView delegate,
                    final String calculationRuleSetID)
    {
        this(delegate);
    }

    public GSINetBasedCalculationResultView_V2(final BaseCalculationResultView delegate)
    {
        super(delegate);
        _delegate = delegate;
    }

    @Override
    public Collection<ComputedProductLineItem> getComputedProductLineItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.PLI.PLIS);
    }


    @Override
    public ComputedProductLineItem getPLI(final String id)
    {
        final Group<ComputedProductLineItem> selector = GSINetBasedCalculation_V2.PLI.PLIS;
        return getPLI(id, selector);
    }

    @Override
    public ComputedProductLineItem getInitialPLI(final String id)
    {
        final Group<ComputedProductLineItem> selector = GSINetBasedCalculation_V2.PLI.ROOT_PLIS;
        return getPLI(id, selector);
    }

    private ComputedProductLineItem getPLI(final String id, final Group<ComputedProductLineItem> selector)
    {
        final Collection<ComputedProductLineItem> cpliList = _delegate.getComputedItems(selector);
        for(final ComputedProductLineItem cpli : cpliList)
        {
            if(cpli.getProductLineItemUUID().equals(id))
            {
                return cpli;
            }
        }
        return null;
    }

    public Money getPLIDiscountedPrice(final String id)
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.PLI.NET_PRICES, id));
    }

    // TODO remove
    @Override
    public Money getPLISingleBasePrice(final String lineItemID)
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.PLI.SINGLE_BASE_PRICES, lineItemID));
    }

    // TODO remove
    @Override
    public Collection<ComputedMoneyItem> getPLIDiscountsOnItemItems()
    {
        return Collections.emptyList();
    }

    @Override
    public Collection<ComputedMoneyItem> getPLIDiscountsOnItemItems(final String id)
    {
        return Collections.emptyList();
    }

    @Override
    public Collection<ComputedMoneyItem> getPLISubTotalItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.PLI.SUBTOTALS);
    }

    @Override
    public Collection<ComputedTaxInfoItem> getPLITaxInfoItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.PLI.TAX_INFO);
    }

    @Override
    public Collection<ComputedPercentageItem> getPLITaxRateItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.PLI.TAX_RATES);
    }

    @Override
    public Collection<ComputedPercentageItem> getPLITaxIncludedRateItems()
    {
        return Collections.emptyList();
    }

    @Override
    public Collection<ComputedMoneyItem> getPLITaxPriceItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.PLI.TAX_PRICES);
    }

    @Override
    public Money getPLISubTotalsDiscount()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.PLI.SUBTOTALS_DISCOUNT));
    }

    @Override
    public Money getPLISubTotalsTax()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.PLI.SUBTOTALS_TAX));
    }

    @Override
    public Money getPLISubTotalsDuty()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.PLI.SUBTOTALS_DUTY));
    }

    @Override
    public Collection<ComputedDiscountItem> getAppliedDiscountsOnItemItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.PROMOTION.APPLIED_DISCOUNTS_ONITEM);
    }

    @Override
    public Collection<ComputedDiscountItem> getAppliedDiscountsOnOrderValueItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.PROMOTION.APPLIED_DISCOUNTS_ONORDERVALUE);
    }

    @Override
    public Collection<ComputedDiscountItem> getAppliedDiscountsOnShippingItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.PROMOTION.APPLIED_DISCOUNTS_ONSHIPPING);
    }

    @Override
    public Collection<ComputedDiscountItem> getAppliedDiscountItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.PROMOTION.APPLIED_DISCOUNTS);
    }

    @Override
    public Collection<ComputedDiscountItem> getAppliedDiscountItems(final String id)
    {
        final Collection<ComputedDiscountItem> itemDiscounts = new ArrayList<ComputedDiscountItem>();
        for(final ComputedDiscountItem cdi : getAppliedDiscountItems())
        {
            if (cdi.getDependsOn().contains(getPLI(id)) && null != cdi.getDiscountRebate()
                            && null != cdi.getDiscountRebate().getPromotion())
            {
                itemDiscounts.add(cdi);
            }
        }
        return itemDiscounts;
    }

    @Override
    public Money getAppliedDiscountAmount(final String id)
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.PROMOTION.APPLIED_DISCOUNT_AMOUNTS, id));
    }

    @Override
    public Money getAppliedDiscountAmountOnItem(final String id)
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.PLI.DISCOUNT_SUMS, id));
    }

    @Override
    public Collection<ComputedMoneyItem> getPLISingleDiscountOnItemItems()
    {
        return Collections.emptyList();
    }

    @Override
    public Money getOrderValueDiscount()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.ORDER.VALUE_DISCOUNT));
    }

    @Override
    public Collection<ComputedDynamicMessageDiscountItem> getDynamicMessageDiscountItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.PROMOTION.DYNAMIC_MESSAGE_DISCOUNTS);
    }

    @Override
    public Collection<ComputedProductLineItem> getBonusProductOnGiftItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.PROMOTION.BONUS_PRODUCTS_ONGIFT);
    }

    @Override
    public Collection<ComputedProductLineItem> getBonusProductOnHiddenGiftItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.PROMOTION.BONUS_PRODUCTS_ONHIDDENGIFT);
    }

    @Override
    public Collection<ComputedShippingExtraChargeItem> getPLIShippingOverrideItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.SHIPPING.PLI_OVERRIDE);
    }

    @Override
    public Collection<ComputedShippingExtraChargeItem> getPLIShippingSurchargeItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.SHIPPING.PLI_SURCHARGES);
    }

    @Override
    public Collection<ComputedShippingExtraChargeItem> getPLIGeographicalSurchargeItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.SHIPPING.PLI_GEOGRAPHICAL_SURCHARGES);
    }

    @Override
    public Collection<ComputedShippingExtraChargeItem> getPLIImportSurchargeItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.SHIPPING.PLI_IMPORT_SURCHARGES);
    }

    @Override
    public Collection<ComputedMoneyItem> getPLIShippingOverrideCostItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.SHIPPING.PLI_OVERRIDE_COST);
    }

    @Override
    public Collection<ComputedMoneyItem> getPLIShippingSurchargeCostItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.SHIPPING.PLI_SURCHARGE_COSTS);
    }

    @Override
    public Collection<ComputedMoneyItem> getPLIGeographicalSurchargeCostItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.SHIPPING.PLI_GEOGRAPHICAL_SURCHARGE_COSTS);
    }

    @Override
    public Collection<ComputedMoneyItem> getPLIImportSurchargeCostItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.SHIPPING.PLI_IMPORT_SURCHARGE_COSTS);
    }

    @Override
    public Collection<ComputedShippingExtraChargeItem> getShippingBucketOverrideItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.SHIPPING.BUCKET_OVERRIDE);
    }

    @Override
    public Collection<ComputedMoneyItem> getShippingBucketOverrideCostItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.SHIPPING.BUCKET_OVERRIDE_COST);
    }

    @Override
    public Collection<ComputedShippingExtraChargeItem> getShippingBucketSurchargeItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.SHIPPING.BUCKET_SURCHARGES);
    }

    @Override
    public Collection<ComputedMoneyItem> getShippingBucketSurchargeCostItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.SHIPPING.BUCKET_SURCHARGE_COSTS);
    }

    @Override
    public Collection<ComputedMoneyItem> getShippingBucketCostsItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.SHIPPING.BUCKET_COSTS);
    }

    @Override
    public Collection<ComputedShippingBucketItem> getShippingBucketItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.SHIPPING.BUCKETS);
    }

    @Override
    public Money getShippingPLICosts(final String id)
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.SHIPPING.PLI_COSTS, id));
    }

    @Override
    public Collection<ComputedMoneyItem> getShippingPLITaxCosts()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.SHIPPING.PLI_TAX_COSTS);
    }

    @Override
    public Collection<ComputedTaxInfoItem> getShippingPLITaxInfoItems()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.SHIPPING.PLI_TAX_INFO);
    }

    @Override
    public Collection<ComputedMoneyItem> getShippingSubTotals()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.SHIPPING.SUBTOTALS);
    }

    @Override
    public Money getTotalShippingTaxes()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.SHIPPING.TOTALS_TAX));
    }

    @Override
    public Collection<ComputedMoneyItem> getTotals()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.TOTAL.TOTALS);
    }

    @Override
    public Money getTotalShipping()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.TOTAL.TOTALS_SHIPPING));
    }

    @Override
    public Money getTotalShippingDiscounts()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.SHIPPING.TOTALS_DISCOUNT));
    }

    @Override
    public Money getTotalDiscounts()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.TOTAL.DISCOUNT));
    }

    @Override
    public Money getTotalTax()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.TOTAL.TAX));
    }

    @Override
    public Money getTotalGross()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.TOTAL.GROSS));
    }

    @Override
    public Money getTotalBonusPointPoints()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.TOTAL.BPP));
    }

    @Override
    public Money getTotalBonusPointValue()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.TOTAL.BPV));
    }

    @Override
    public Money getPLIPrices(final String id)
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.PLI.NET_BASE_PRICES, id));
    }

    @Override
    public Money getPLISubTotal()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.PLI.SUBTOTALS_NET));
    }

    public Money getPLIDiscountedSubTotal()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.PLI.SUBTOTALS_NET_WITH_DISCOUNT));
    }

    @Override
    public Money getShippingSubTotal()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.SHIPPING.TOTALS_NET));
    }

    // FIXME
    @Override
    public Money getHandlingSubTotal()
    {
        return Money.NOT_AVAILABLE;
    }

    @Override
    public void getSingleItemShippingPrice(final Map<String, Money> map, final ComputedProductLineItem cpli)
    {
        ComputedMoneyItem cmi = _delegate.getComputedItem(GSINetBasedCalculation_V2.SHIPPING.PLI_COSTS, cpli.getID());
        for(final ComputedItem ci : cmi.getDependsOn())
        {
            if (!ci.getGroup().equals(GSINetBasedCalculation_V2.PLI.PLIS.getGroup())
                            && !ci.getGroup().equals(GSINetBasedCalculation_V2.SHIPPING.BUCKETS.getGroup()))
            {
                cmi = _delegate.getComputedItem(ci.getGroup(), cpli.getID());
                map.put(cpli.getProductLineItemUUID(), cmi.getMoney());
                break;
            }
        }
    }

    @Override
    public Collection<Discount> getAppliedDiscountsOnItem(final String lineItemID)
    {
        // get distributed order discounts
        final Collection<Discount> discounts = getAppliedDiscounts(
                        GSINetBasedCalculation_V2.PROMOTION.APPLIED_DISCOUNTS_ONORDERVALUE,
                        GSINetBasedCalculation_V2.PROMOTION.PLI_DISCOUNTS_ONORDERVALUE,
                        lineItemID);

        // get item discounts
        discounts.addAll(getAppliedDiscounts(
                        GSINetBasedCalculation_V2.PROMOTION.APPLIED_DISCOUNTS_ONITEM,
                        GSINetBasedCalculation_V2.PROMOTION.PLI_DISCOUNTS_ONITEM,
                        lineItemID));

        return discounts;
    }


    @Override
    public Collection<Discount> getAppliedDiscountsOnShipping(final String lineItemID)
    {
        // get distributed order discounts and item discounts
        return getAppliedDiscounts(
                        GSINetBasedCalculation_V2.PROMOTION.APPLIED_DISCOUNTS_ONSHIPPING,
                        GSINetBasedCalculation_V2.PROMOTION.PLI_DISCOUNTS_ONSHIPPING,
                        lineItemID);
    }

    @Override
    public Money getTaxAmount(final String lineItemID)
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.PLI.TAXES, lineItemID));
    }

    @Override
    public Collection<GSITaxInformationBO> getItemPriceBasedTaxes(final String lineItemID)
    {
        return getTaxes(GSINetBasedCalculation_V2.PLI.TAX_INFO, lineItemID);
    }

    @Override
    public Collection<GSITaxInformationBO> getShippingCostBasedTaxes(final String lineItemID)
    {
        return getTaxes(GSINetBasedCalculation_V2.SHIPPING.PLI_TAX_INFO, lineItemID);
    }

    @Override
    public Money getDutyAmount(final String lineItemID)
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.PLI.DUTIES, lineItemID));
    }

    @Override
    public Collection<GSITaxInformationBO> getDuties(final String lineItemID)
    {
        return getTaxes(GSINetBasedCalculation_V2.PLI.DUTY_INFO, lineItemID);
    }

    @Override
    public Money getFeesAmount(final String lineItemID)
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.PLI.SURCHARGES, lineItemID));
    }

    @Override
    public Collection<GSIExtraChargeInformationBO> getGeographicalFees(final String lineItemID)
    {
        return getExtraSurcharges(GSINetBasedCalculation_V2.SHIPPING.PLI_GEOGRAPHICAL_SURCHARGES,
                        GSINetBasedCalculation_V2.SHIPPING.PLI_GEOGRAPHICAL_SURCHARGE_COSTS,
                        GSINetBasedCalculation_V2.SHIPPING.PLI_GEOGRAPHICAL_SURCHARGE_TAX_INFO, lineItemID);
    }

    @Override
    public Collection<GSIExtraChargeInformationBO> getImportFees(final String lineItemID)
    {
        return getExtraSurcharges(GSINetBasedCalculation_V2.SHIPPING.PLI_IMPORT_SURCHARGES,
                        GSINetBasedCalculation_V2.SHIPPING.PLI_IMPORT_SURCHARGE_COSTS,
                        GSINetBasedCalculation_V2.SHIPPING.PLI_IMPORT_SURCHARGE_TAX_INFO, lineItemID);
    }

    /**
     * Get totalDiscount for given <code>lineItemID</code>.
     * Kind of discount (e.g. shipping) depends from given <code>cmiSelector, cdiSelector</code>.
     *
     * @param cdiSelector for computed discount items
     * @param cmiSelector for total discounts
     * @param lineItemID
     * @return a list of discounts
     */
    private Collection<Discount> getAppliedDiscounts(
                    final CellSelector.Group<ComputedDiscountItem> cdiSelector,
                    final CellSelector.GroupSet<ComputedMoneyItem> cmiSelector,
                    final String lineItemID)

    {
        if (cmiSelector == null || cdiSelector == null)
        {
            return new ArrayList<Discount>();
        }

        if (_delegate.getComputedItems(cdiSelector).isEmpty())
        {
            return new ArrayList<Discount>();
        }

        final Collection<ComputedMoneyItem> cmis = _delegate.getComputedItems(cmiSelector);
        if (cmis.isEmpty())
        {
            return new ArrayList<Discount>();
        }

        final List<Discount> result = new ArrayList<Discount>();
        for(final ComputedMoneyItem cmi : cmis)
        {
            final Set<ComputedItem> cplis = cmi.getDependsOn(GSINetBasedCalculation_V2.PLI.PLIS.getGroup());
            final Set<ComputedItem> cdis = cmi.getDependsOn(cdiSelector.getGroup());
            if (cplis != null && cplis.size() > 0 &&
                            cdis != null && cdis.size() > 0 &&
                            cplis.iterator().next().getID().equalsIgnoreCase(lineItemID))
            {
                final ComputedDiscountItem cdi = (ComputedDiscountItem)cdis.iterator().next();
                if (null != cdi.getDiscountRebate() && null != cdi.getDiscountRebate().getPromotion())
                {
                    //create new GSIDiscount(cdi, money)
                    result.add(new GSIDiscount(cdi, cmi.getMoney()));
                }
            }
        }
        return result;
    }

    private Collection<GSITaxInformationBO> getTaxes(final CellSelector.GroupSet<ComputedTaxInfoItem> selector,
                    final String lineItemID)
    {
        if (selector == null)
        {
            throw new IllegalArgumentException("Parameter [selector] must not be null!");
        }
        if (lineItemID == null)
        {
            throw new IllegalArgumentException("Parameter [final String lineItemID] must not be null!");
        }

        final Collection<ComputedTaxInfoItem> taxes = _delegate.getComputedItems(selector);
        if (taxes.isEmpty())
        {
            return Collections.emptyList();
        }

        final List<GSITaxInformationBO> result = new ArrayList<GSITaxInformationBO>(taxes.size());
        for(final ComputedTaxInfoItem ctii : taxes)
        {
            if (ctii.getID().equalsIgnoreCase(lineItemID))
            {
                result.add(new GSITaxInformationBOImpl(ctii));
            }
        }
        return result;
    }

    /**
     * Provides information about extra costs charged for the specified line
     * item.
     *
     * @param cseciSelector
     *            selector for retrieval of <code>ComputedExtraChargeItem</code>
     *            instances representing the extra surcharges
     * @param cmiSelector
     *            selector for retrieval of <code>ComputedMoneyItem</code>
     *            instances containing extra surcharge cost amounts
     * @param ctiiSelector
     *            selector for retrieval of <code>ComputedTaxInfoItem</code>
     *            instances containing all taxes applied to extra surcharges
     * @param cpli
     *            <code>ComputedProductLineItem</code> instance representing the
     *            line item, the extra costs are charged for
     *
     * @return
     */
    private Collection<GSIExtraChargeInformationBO> getExtraSurcharges(
                    final CellSelector.GroupSet<ComputedShippingExtraChargeItem> cseciSelector,
                    final CellSelector.GroupSet<ComputedMoneyItem> cmiSelector,
                    final CellSelector.GroupSet<ComputedTaxInfoItem> ctiiSelector, final String lineItemID)
    {
        if (cseciSelector == null)
        {
            throw new IllegalArgumentException("Parameter [selector] must not be null!");
        }

        final Collection<ComputedShippingExtraChargeItem> fees = _delegate.getComputedItems(cseciSelector);
        if (fees.isEmpty())
        {
            return Collections.emptyList();
        }

        final List<GSIExtraChargeInformationBO> result = new ArrayList<GSIExtraChargeInformationBO>(fees.size());
        for(final ComputedShippingExtraChargeItem cseci : fees)
        {
            if (cseci.getID().equalsIgnoreCase(lineItemID))
            {
                final ComputedMoneyItem cmi = _delegate.getComputedItem(
                                cmiSelector.getGroupPrefix() + cseciSelector.getGroupExtension(cseci.getGroup()),
                                cseci.getID());
                result.add(new GSIExtraChargeInformationBOImpl(cseci, cmi, getExtraSurchargeBasedTaxes(ctiiSelector,
                                cseci)));
            }
        }
        return result;
    }

    /**
     * Provides information about all taxes applied to the specified extra
     * surcharge.
     *
     * @param ctiiSelector
     * @param csesi
     *            <code>ComputedTaxInfoItem</code> instance representing the
     *            extra surcharge the taxes are applied to
     * @return
     */
    private Collection<ComputedTaxInfoItem> getExtraSurchargeBasedTaxes(
                    final CellSelector.GroupSet<ComputedTaxInfoItem> ctiiSelector,
                    final ComputedShippingExtraChargeItem csesi)
    {
        if (csesi == null)
        {
            throw new IllegalArgumentException("Parameter [csesi] must not be null!");
        }

        final Collection<ComputedTaxInfoItem> taxes = _delegate.getComputedItems(ctiiSelector);
        if (taxes.isEmpty())
        {
            return Collections.emptyList();
        }

        final List<ComputedTaxInfoItem> result = new ArrayList<ComputedTaxInfoItem>();
        for(final ComputedTaxInfoItem ctii : taxes)
        {
            if (ctii.getDependsOn().contains(csesi))
            {
                result.add(ctii);
            }
        }
        return result;
    }

    @Override
    public ComputedMoneyItem getPLIShippingSurchargeCostItem(final ComputedShippingExtraChargeItem csec)
    {
        return getShippingCostitem(csec, GSINetBasedCalculation_V2.SHIPPING.PLI_SURCHARGE_COSTS, GSINetBasedCalculation_V2.SHIPPING.PLI_SURCHARGES);
    }

    private ComputedMoneyItem getShippingCostitem(final ComputedShippingExtraChargeItem csec,
                    final GroupSet<?> costs, final GroupSet<?> surcharges)
    {
        if (csec == null)
        {
            return null;
        }

        final ComputedMoneyItem cmi = _delegate.getComputedItem(
                        costs.getGroupPrefix()
                                        + surcharges.getGroupExtension(csec
                                                        .getGroup()), csec.getID());

        return cmi;
    }

    @Override
    public ComputedMoneyItem getPLIShippingOverrideCostItem(final ComputedShippingExtraChargeItem csec)
    {
        return getShippingCostitem(csec, GSINetBasedCalculation_V2.SHIPPING.PLI_OVERRIDE_COST, GSINetBasedCalculation_V2.SHIPPING.PLI_OVERRIDE);
    }

    @Override
    public ComputedMoneyItem getPLIGeographicalSurchargeCostItem(final ComputedShippingExtraChargeItem csec)
    {
        return getShippingCostitem(csec, GSINetBasedCalculation_V2.SHIPPING.PLI_GEOGRAPHICAL_SURCHARGE_COSTS, GSINetBasedCalculation_V2.SHIPPING.PLI_GEOGRAPHICAL_SURCHARGES);
    }

    @Override
    public ComputedMoneyItem getPLIImportSurchargeCostItem(final ComputedShippingExtraChargeItem csec)
    {
        return getShippingCostitem(csec, GSINetBasedCalculation_V2.SHIPPING.PLI_IMPORT_SURCHARGE_COSTS, GSINetBasedCalculation_V2.SHIPPING.PLI_IMPORT_SURCHARGES);
    }

    @Override
    public ComputedMoneyItem getShippingBucketOverrideCostItem(final ComputedShippingExtraChargeItem csec)
    {
        return getShippingCostitem(csec, GSINetBasedCalculation_V2.SHIPPING.PLI_OVERRIDE_COST, GSINetBasedCalculation_V2.SHIPPING.PLI_OVERRIDE_COST);
    }

    @Override
    public ComputedMoneyItem getShippingBucketSurchargeCostItem(final ComputedShippingExtraChargeItem csec)
    {
        return getShippingCostitem(csec, GSINetBasedCalculation_V2.SHIPPING.PLI_SURCHARGE_COSTS, GSINetBasedCalculation_V2.SHIPPING.PLI_SURCHARGE_COSTS);
    }

    @Override
    public Collection<ComputedShippingExtraChargeItem> getPLIGeographicalSurchargeItems(
                    final ComputedProductLineItem cpli)
    {
        final Collection<ComputedShippingExtraChargeItem> cGeoSurcharges = _delegate
                        .getComputedItems(GSINetBasedCalculation_V2.SHIPPING.PLI_GEOGRAPHICAL_SURCHARGES);
        return getShippingExtraChargeItem(cpli, cGeoSurcharges);
    }

    @Override
    public Collection<ComputedShippingExtraChargeItem> getPLIShippingOverrideItems(final ComputedProductLineItem cpli)
    {
        final Collection<ComputedShippingExtraChargeItem> cOverrides = _delegate
                        .getComputedItems(GSINetBasedCalculation_V2.SHIPPING.PLI_OVERRIDE);
        return getShippingExtraChargeItem(cpli, cOverrides);
    }

    @Override
    public Collection<ComputedShippingExtraChargeItem> getPLIShippingSurchargeItems(final ComputedProductLineItem cpli)
    {
        final Collection<ComputedShippingExtraChargeItem> cSurcharges = _delegate
                        .getComputedItems(GSINetBasedCalculation_V2.SHIPPING.PLI_SURCHARGES);
        return getShippingExtraChargeItem(cpli, cSurcharges);
    }

    @Override
    public Collection<ComputedShippingExtraChargeItem> getPLIImportSurchargeItems(final ComputedProductLineItem cpli)
    {
        final Collection<ComputedShippingExtraChargeItem> cImportSurcharges = _delegate
                        .getComputedItems(GSINetBasedCalculation_V2.SHIPPING.PLI_IMPORT_SURCHARGES);
        return getShippingExtraChargeItem(cpli, cImportSurcharges);
    }

    @Override
    public Collection<ComputedShippingExtraChargeItem> getShippingBucketOverrideItems(
                    final ComputedShippingBucketItem cbi)
    {
        final Collection<ComputedShippingExtraChargeItem> cOverrides = _delegate
                        .getComputedItems(GSINetBasedCalculation_V2.SHIPPING.BUCKET_OVERRIDE);
        return getShippingExtraChargeItem(cbi, cOverrides);
    }

    private Collection<ComputedShippingExtraChargeItem> getShippingExtraChargeItem(final ComputedItem cpli,
                    final Collection<ComputedShippingExtraChargeItem> chargeItems)
    {
        final Collection<ComputedShippingExtraChargeItem> cAssignedGeoSurcharges = new HashSet<ComputedShippingExtraChargeItem>();
        if (chargeItems == null || chargeItems.isEmpty())
        {
            return null;
        }

        final Iterator<ComputedShippingExtraChargeItem> cGeoSurchargesIterator = chargeItems.iterator();
        while(cGeoSurchargesIterator.hasNext())
        {
            final ComputedShippingExtraChargeItem cGeoExtraCharge = cGeoSurchargesIterator.next();
            if (cGeoExtraCharge.getID().equals(cpli.getID()))
            {
                cAssignedGeoSurcharges.add(cGeoExtraCharge);
            }
        }
        return cAssignedGeoSurcharges;
    }

    @Override
    public Collection<ComputedShippingExtraChargeItem> getShippingBucketSurchargeItems(
                    final ComputedShippingBucketItem cbi)
    {
        final Collection<ComputedShippingExtraChargeItem> cSurcharges = _delegate
                        .getComputedItems(GSINetBasedCalculation_V2.SHIPPING.BUCKET_SURCHARGES);
        return getShippingExtraChargeItem(cbi, cSurcharges);
    }

    @Override
    public Money getTotalDutiesAndFees()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.TOTAL.DUTY_SURCHARGE));
    }

    @Override
    public Collection<CostInformationBO> getTaxGroups()
    {
        final Collection<CostInformationBO> taxGroups = new HashSet<CostInformationBO>();
        final Iterator<ComputedMoneyItem> cGroupsIterator = _delegate.getComputedItems(
                        GSINetBasedCalculation_V2.TOTAL.TAX_PER_TYPE).iterator();
        while(cGroupsIterator.hasNext())
        {
            taxGroups.add(getCostInformation(cGroupsIterator.next()));
        }
        return taxGroups;
    }

    @Override
    public Collection<CostInformationBO> getDutiesAndFeesGroups()
    {
        final Collection<CostInformationBO> taxGroups = new HashSet<CostInformationBO>();
        Iterator<ComputedMoneyItem> cGroupsIterator = _delegate.getComputedItems(
                        GSINetBasedCalculation_V2.TOTAL.DUTY_PER_TYPE).iterator();
        while(cGroupsIterator.hasNext())
        {
            taxGroups.add(getCostInformation(cGroupsIterator.next()));
        }
        cGroupsIterator = _delegate.getComputedItems(GSINetBasedCalculation_V2.TOTAL.SURCHARGE_PER_TYPE).iterator();
        while(cGroupsIterator.hasNext())
        {
            taxGroups.add(getCostInformation(cGroupsIterator.next()));
        }
        return taxGroups;
    }

    private CostInformationBO getCostInformation(final ComputedMoneyItem cmi)
    {
        final StringTokenizer description = new StringTokenizer(cmi.getDescription(), "(", false);
        final String displayName = description.nextToken();
        final String shortDisplayName = description.hasMoreTokens() ? description.nextToken(")") : displayName;
        return new CostInformationBOImpl(displayName, shortDisplayName, cmi.getMoney());
    }

    @Override
    public ComputedErrorItem getTaxServiceErrorItem()
    {
        return _delegate.getComputedItem(GSINetBasedCalculation_V2.TAX.ERROR_TAX_DUTY_FEE);
    }

    @Override
    public Money getWarrantyLineItemTotal(final ProductLineItem parentPLI)
    {
        final SubGroup<ComputedProductLineItem, ComputedMoneyItem> subGroup = GSINetBasedCalculation_V2.WARRANTY.PLI_NET_PRICES;
        return getAmount(getFirstChild(parentPLI.getUUID(), subGroup));
    }

    @Override
    public Money getWarrantySingelBasePrice(final ProductLineItem parentPLI)
    {
        final SubGroup<ComputedProductLineItem, ComputedMoneyItem> subGroup = GSINetBasedCalculation_V2.WARRANTY.PLI_SINGLE_PRICES;
        return getAmount(getFirstChild(parentPLI.getUUID(), subGroup));
    }

    @Override
    public Money getWarrantyTotal()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.WARRANTY.TOTALS_NET));
    }

    private <T extends ComputedItem> T getFirstChild(final String rootPLIId, final SubGroup<ComputedProductLineItem, T> subGroup)
    {
        final Collection<ComputedProductLineItem> parents = _delegate.getComputedItems(GSINetBasedCalculation_V2.PLI.ROOT_PLIS);
        ComputedProductLineItem parent = null;
        for(final ComputedProductLineItem item : parents)
        {
            if (item.getProductLineItemUUID().equals(rootPLIId))
            {
                parent  = item;
                break;
            }
        }
        if (parent == null)
        {
            return null;
        }

        final String group = subGroup.getGroup(parent);
        final Collection<T> cItems = _delegate.getComputedItems(group);
        if (cItems != null && cItems.size() > 0)
        {
            return cItems.iterator().next();
        }
        else
        {
            return null;
        }
    }

    @Override
    public Collection<ComputedProductLineItem> getGiftOptions()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.GIFTING.FILTERED_PLIS);
    }

    @Override
    public Money getTotalGiftOptions()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.GIFTING.TOTALS_NET));
    }

    @Override
    public Money getGiftOptionTotal(final ComputedProductLineItem cgiftOptionPLI)
    {
        if(cgiftOptionPLI == null)
        {
            return Money.NOT_AVAILABLE;
        }

        final Collection<? extends ComputedItem> cItems = _delegate.getComputedItems(GSINetBasedCalculation_V2.GIFTING.PLI_NET_PRICES);

        for(final ComputedItem cItem: cItems)
        {
           if(cItem.getID().equals(cgiftOptionPLI.getID()))
        {
            return getAmount((ComputedMoneyItem)cItem);
        }
        }
        return Money.NOT_AVAILABLE;
    }

    @Override
    public Money getGiftOptionSingleBasePrice(final ComputedProductLineItem cgiftOptionPLI)
    {
        if(cgiftOptionPLI == null)
        {
            return null;
        }

        final Collection<? extends ComputedItem> cItems = _delegate.getComputedItems(GSINetBasedCalculation_V2.GIFTING.PLI_SINGLE_PRICES);

        for(final ComputedItem cItem: cItems)
        {
           if(cItem.getID().equals(cgiftOptionPLI.getID()))
        {
            return getAmount((ComputedMoneyItem)cItem);
        }
        }
        return Money.NOT_AVAILABLE;
    }

    @Override
    public Money getProductConfigurationNetPrice(final ProductLineItem productLineItem)
    {
        final SubGroup<ComputedProductLineItem, ComputedProductLineItem> filteredPLIs = GSINetBasedCalculation_V2.CONFIGURATION.FILTERED_PLIS;
        final SubGroup<ComputedProductLineItem, ComputedMoneyItem> netPrices = GSINetBasedCalculation_V2.CONFIGURATION.PLI_NET_PRICES;
        return getProductConfigurationNetPrice(productLineItem, filteredPLIs, netPrices);
    }

    @Override
    public Money getGiftOptionTaxPrice(final ComputedProductLineItem cgiftOptionPLI)
    {
        if(cgiftOptionPLI == null)
        {
            return null;
        }

        final Collection<? extends ComputedItem> cItems = _delegate.getComputedItems(GSINetBasedCalculation_V2.GIFTING.PLI_TAXES);

        for(final ComputedItem cItem: cItems)
        {
           if(cItem.getID().equals(cgiftOptionPLI.getID()))
        {
            return getAmount((ComputedMoneyItem)cItem);
        }
        }
        return Money.NOT_AVAILABLE;
    }

    @Override
    public Money getPLINetBasePrice(final String lineItemID)
    {
        return getPLIPrices(lineItemID);
    }

    @Override
    public Money getGiftOptionNetPrice(final String lineItemID)
    {
        return getGiftOptionTotal(getPLI(lineItemID));
    }

    @Override
    public Money getShippingPLINetCosts(final String lineItemID)
    {
        return getShippingPLICosts(lineItemID);
    }

    @Override
    public Money getWarrantyLineItemNetTotal(final String parentPLIID)
    {
        return getWarrantyLineItemTotal(getPLI(parentPLIID).getProductLineItem());
    }

    @Override
    public Collection<ComputedTaxInfoItem> getShippingTaxDiscountInfoItems()
    {
        return Collections.emptyList();
    }

    @Override
    public Collection<ComputedTaxInfoItem> getPLITaxDiscountInfoItems()
    {
        return Collections.emptyList();
    }

    /*
     * payment connected section starts here
     */

    @Override
    public Money getPaymentCosts()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.PAYMENT.TOTALS_NET));
    }

    @Override
    public Money getPaymentCost(final PaymentInstrumentInfo pii)
    {
        if (pii.getPaymentService().getCapabilities().isOpenTender())
        {
            final Group<ComputedPaymentInstrumentItem> piis = GSINetBasedCalculation_V2.PAYMENT.OPEN_TENDER_PIIS;
            final Group<ComputedMoneyItem> prices = GSINetBasedCalculation_V2.PAYMENT.OPEN_TENDER_PIIS_PAYMENT_COSTS;
            return getPIIBasedAmount(pii, piis, prices);
        }
        else
        {
            final Group<ComputedPaymentInstrumentItem> piis = GSINetBasedCalculation_V2.PAYMENT.LIMITED_TENDER_PIIS;
            final Group<ComputedMoneyItem> prices = GSINetBasedCalculation_V2.PAYMENT.LIMITED_TENDER_PIIS_PAYMENT_COSTS;
            return getPIIBasedAmount(pii, piis, prices);
        }
    }


    @Override
    public Money getPaymentTaxes()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.PAYMENT.TOTALS_TAX));
    }


    @Override
    public Money getPaymentBaseAmount(final PaymentInstrumentInfo pii)
    {
        if (pii.getPaymentService().getCapabilities().isOpenTender())
        {
            final Group<ComputedPaymentInstrumentItem> piis = GSINetBasedCalculation_V2.PAYMENT.OPEN_TENDER_PIIS;
            final Group<ComputedMoneyItem> prices = GSINetBasedCalculation_V2.PAYMENT.OPEN_TENDER_PIIS_BASEAMOUNT;
            return getPIIBasedAmount(pii, piis, prices);
        }
        else
        {
            final Group<ComputedPaymentInstrumentItem> piis = GSINetBasedCalculation_V2.PAYMENT.LIMITED_TENDER_PIIS;
            final Group<ComputedMoneyItem> prices = GSINetBasedCalculation_V2.PAYMENT.LIMITED_TENDER_PIIS_BASEAMOUNT;
            return getPIIBasedAmount(pii, piis, prices);
        }
    }

    @Override
    public Money getPaymentSurcharges()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.PAYMENT.TOTALS_SURCHARGES));
    }

    @Override
    public Money getPaymentSurcharges(final PaymentInstrumentInfo pii)
    {
        if (pii.getPaymentService().getCapabilities().isOpenTender())
        {
            final Group<ComputedPaymentInstrumentItem> piis = GSINetBasedCalculation_V2.PAYMENT.OPEN_TENDER_PIIS;
            final Group<ComputedMoneyItem> prices = GSINetBasedCalculation_V2.PAYMENT.OPEN_TENDER_PIIS_SURCHARGES;
            return getPIIBasedAmount(pii, piis, prices);
        }
        else
        {
            final Group<ComputedPaymentInstrumentItem> piis = GSINetBasedCalculation_V2.PAYMENT.LIMITED_TENDER_PIIS;
            final Group<ComputedMoneyItem> prices = GSINetBasedCalculation_V2.PAYMENT.LIMITED_TENDER_PIIS_SURCHARGES;
            return getPIIBasedAmount(pii, piis, prices);
        }
    }

    @Override
    public Money getPaymentTax(final PaymentInstrumentInfo pii)
    {
        if (pii.getPaymentService().getCapabilities().isOpenTender())
        {
            final Group<ComputedPaymentInstrumentItem> piis = GSINetBasedCalculation_V2.PAYMENT.OPEN_TENDER_PIIS;
            final Group<ComputedMoneyItem> prices = GSINetBasedCalculation_V2.PAYMENT.OPEN_TENDER_PIIS_PAYMENT_TAXES;
            return getPIIBasedAmount(pii, piis, prices);
        }
        else
        {
            final Group<ComputedPaymentInstrumentItem> piis = GSINetBasedCalculation_V2.PAYMENT.LIMITED_TENDER_PIIS;
            final Group<ComputedMoneyItem> prices = GSINetBasedCalculation_V2.PAYMENT.LIMITED_TENDER_PIIS_PAYMENT_TAXES;
            return getPIIBasedAmount(pii, piis, prices);
        }
    }

    @Override
    public Money getPaymentTotalAmount()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.PAYMENT.TOTALS_COVERED));
    }

    @Override
    public Money getPaymentTotalAmount(final PaymentInstrumentInfo pii)
    {
        if (pii.getPaymentService().getCapabilities().isOpenTender())
        {
            final Group<ComputedPaymentInstrumentItem> piis = GSINetBasedCalculation_V2.PAYMENT.OPEN_TENDER_PIIS;
            final Group<ComputedMoneyItem> prices = GSINetBasedCalculation_V2.PAYMENT.OPEN_TENDER_PIIS_TOTAL;
            return getPIIBasedAmount(pii, piis, prices);
        }
        else
        {
            final Group<ComputedPaymentInstrumentItem> piis = GSINetBasedCalculation_V2.PAYMENT.LIMITED_TENDER_PIIS;
            final Group<ComputedMoneyItem> prices = GSINetBasedCalculation_V2.PAYMENT.LIMITED_TENDER_PIIS_TOTAL;
            return getPIIBasedAmount(pii, piis, prices);
        }
    }
    
    @Override
    public Money getLimitedTenderTotal()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.PAYMENT.TOTALS_COVERED_LIMITED));
    }

    @Override
    public Money getBasketTotalPriceMinusLimitedTender()
    {
        return getAmount(_delegate.getComputedItem(GSINetBasedCalculation_V2.TOTAL.TOTALS_GROSS_MINUS_LIMITED_PII));
    }

    /*
     * payment connected section ends here
     */

    @Override
    public Collection<ComputedMoneyItem> getNetDiscountAmounts()
    {
        return _delegate.getComputedItems(GSINetBasedCalculation_V2.PROMOTION.NET_DISCOUNT_AMOUNTS);
    }
}